package com.example.armmvvm.ui.fallview

import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.lifecycle.LifecycleOwner
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import timber.log.Timber
import kotlin.coroutines.coroutineContext

/**
 *  author : yanghaozhang
 *  date : 2022/10/19 11:26
 *  description :
 */
class BottomFallViewController private constructor(viewContainer: ViewGroup, lifecycleOwner: LifecycleOwner) :
    BaseFallViewController<BottomFallViewSetting>(viewContainer, lifecycleOwner) {

    companion object {
        private val OFFICIAL_FALL_VIEW_HELPER_LIST: MutableList<BottomFallViewController> = mutableListOf()

        fun getInstance(viewContainer: ViewGroup, lifecycleOwner: LifecycleOwner): BottomFallViewController {
            OFFICIAL_FALL_VIEW_HELPER_LIST.forEach {
                if (it.lifecycleOwner == lifecycleOwner && viewContainer == it.viewContainer) {
                    return it
                }
            }

            return BottomFallViewController(viewContainer, lifecycleOwner).also {
                OFFICIAL_FALL_VIEW_HELPER_LIST.add(it)
            }
        }
    }

    private val behaviorCallBack = BottomFallViewStateCallBack()

    val behavior: BottomSheetBehavior<View> = BottomSheetBehavior.from(viewContainer)

    var currentFallView: BottomFallViewSetting? = null

    init {
        lifecycleOwner.lifecycle.addObserver(this)
    }

    override suspend fun showBottomView(
        fallView: BottomFallViewSetting,
        @BottomSheetBehavior.State state: Int
    ) {
        if (isDestroy()) {
            return
        }
        doingJob?.cancel()
        doingJob = coroutineContext[Job]
        val oldFallView = currentFallView
        currentFallView = fallView
        val container = viewContainer
        when {
            container.indexOfChild(fallView.view) == 0 -> {
                oldFallView?.hideCallBack?.invoke()
                behaviorCallBack.reset()
                fallView.behaviorListener?.invoke(behaviorCallBack)
                behavior.setBottomSheetCallback(behaviorCallBack)
                val curState: Int = when {
                    oldFallView?.mode == FallViewMode.ONE_STATE -> state
                    // 展开状态时切换,不改变当前的state
                    behavior.state == BottomSheetBehavior.STATE_EXPANDED -> BottomSheetBehavior.STATE_EXPANDED
                    else -> state
                }
                // 避免因为界面高度不同,高->矮时,FallView没有重新layout,底部留白
                if (BehaviorLieScroll.lieScrollRefresh(behavior, viewContainer)) {
                    // 如果lieScrollRefresh失败则requestLayout,不过会很生硬
                    viewContainer.requestLayout()
                }
                // 等待刷新界面,才能获取界面高度
                fallView.view.awaitPost(SHOW_TIME_POST_DELAYED)
                showViewInternal(curState, fallView)
                if (behavior.state == curState) {
                    executeBehaviorCallBack(curState)
                }
            }
            container.childCount > 0 -> {
                hideOldBottomView(oldFallView)
                initNewBottomView(fallView)
                fallView.view.awaitPost(SHOW_TIME_POST_DELAYED)
                showViewInternal(state, fallView)
            }
            else -> {
                // 没有子View时,是否需要调用一次hide
                if (fallView.shouldHideBottomView) {
                    hideOldBottomView(oldFallView)
                }
                initNewBottomView(fallView)
                delay(SHOW_TIME_POST_DELAYED)
                showViewInternal(state, fallView)
            }
        }
    }

    override fun hideBottomView(): Boolean {
        doingJob?.cancel()
        return if (currentFallView != null) {
            doingJob = coroutineScope.launch {
                if (currentFallView != null) {
                    hideOldBottomView(currentFallView)
                    currentFallView = null
                }
                delay(DISMISS_TIME_POST_DELAYED)
                viewContainer.removeAllViews()
                viewContainer.visibility = View.GONE
            }
            true
        } else {
            false
        }
    }

    override fun isShowing(): Boolean = currentFallView != null && behavior.state != BottomSheetBehavior.STATE_HIDDEN

    override fun onDestroy() {
        super.onDestroy()
        currentFallView = null
        OFFICIAL_FALL_VIEW_HELPER_LIST.remove(this)
    }

    /**
     * FallView因为visible改变了,所以高度发生了改变,但因为behavior高度没有变化
     * 重新计算高度,重新设置state
     */
    fun recalculate() {
        val fallView = currentFallView ?: return
        doingJob?.cancel()
        doingJob = coroutineScope.launch {
            fallView.view.awaitPost(if (behavior.state >= BottomSheetBehavior.STATE_EXPANDED) DISMISS_TIME_POST_DELAYED else -1)
            Timber.d("Runnable behavior.state::${behavior.state}")
            if (fallView == currentFallView && behavior.state >= BottomSheetBehavior.STATE_EXPANDED) {
                showViewInternal(behavior.state, fallView)
            } else if (fallView == currentFallView) {
                recalculate()
            }
        }
    }

    /**
     * 主动调用一次Behavior的回调
     * 因为当切换FallView时,那么可能不会回调behavior.mCallback
     * hide FallView时,需要回调一次hide,让界面发生变化,然后马上设置[behavior.mCallback]为null
     */
    private fun executeBehaviorCallBack(state: Int) {
        // 不计算onSlide百分比数值
        behaviorCallBack.onSlide(viewContainer, 0f)
        behaviorCallBack.onStateChanged(viewContainer, state)
    }

    private fun hideOldBottomView(oldFallView: BottomFallViewSetting?) {
        behavior.isHideable = true
        behavior.state = BottomSheetBehavior.STATE_HIDDEN
        executeBehaviorCallBack(BottomSheetBehavior.STATE_HIDDEN)
        behaviorCallBack.reset()
        oldFallView?.hideCallBack?.invoke()
    }

    private fun initNewBottomView(fallView: BottomFallViewSetting) {
        val bottomView = fallView.view
        viewContainer.removeAllViews()
        viewContainer.addView(bottomView, bottomView.layoutParams)
        viewContainer.visibility = View.VISIBLE
        behaviorCallBack.reset()
        fallView.behaviorListener?.invoke(behaviorCallBack)
        behavior.setBottomSheetCallback(behaviorCallBack)
    }

    private fun showViewInternal(state: Int, fallView: BottomFallViewSetting) {
        val view = fallView.view
        if (view.height == 0) {
            view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    view.viewTreeObserver.removeOnGlobalLayoutListener(this)
                    updateBehaviorState(fallView, state)
                }
            })
        } else {
            updateBehaviorState(fallView, state)
        }
    }

    private fun updateBehaviorState(fallView: BottomFallViewSetting, state: Int) {
        fallView.calcHeight?.invoke(fallView.view)
        if (fallView.mode == FallViewMode.ONE_STATE) {
            behavior.setPeekHeight(fallView.view.height, true)
        } else {
            behavior.setPeekHeight(fallView.peekHeight, true)
            behavior.halfExpandedRatio = fallView.middleHeightCalc.calcBehaviorHalfExpandedRatio(viewContainer.parent as View)
            behavior.expandedOffset =
                if (fallView.mode == FallViewMode.THREE_NO_TOP_STATE) {
                    maxOf(0, (viewContainer.parent as View).height - fallView.view.height)
                } else {
                    fallView.expandedOffset
                }
        }
        behavior.isDraggable = fallView.mode != FallViewMode.ONE_STATE
        behavior.isFitToContents = fallView.mode != FallViewMode.THREE_STATE && fallView.mode != FallViewMode.THREE_NO_TOP_STATE
        behavior.state = state
        behavior.isHideable = fallView.hideable
    }
}